# -*- coding: utf-8 -*-
'''
@File    :   hex_reader.py
@Time    :   2024/08/10 20:58:14
@Author  :   None
@Version :   1.0
@Desc    :   解析嵌入式固件编译后生成的.hex文件
'''

import sys
import typing

from .checksum import CRCType, crc_cal
from .datatypes import MemoryData
from .logger import get_logger


class HexIO:
    '''
    这个类可以设计成 hex_io
    '''
    def __init__(self, hex_path:str):
        self._logger = get_logger()
        self._hex_path:typing.Optional[str]                                   = hex_path         # hex文件路径
        self._linear_address_map_memory_data:typing.Dict[int, MemoryData]     = {}               # 线性地址映射到地址再映射到数据
        self._parse()
    
    def _parse(self) -> None:
        if (self._hex_path is None):
            self._logger.error("hex文件路径为空")
            return
        f = open(self._hex_path, 'r', encoding='utf-8')
        
        current_start_linear_address:int = 0x0000
        prev_abs_byte_address:int = 0x00000000
        for index, line in enumerate(f.readlines()):
            length:int = 0x00
            address:int = 0x00
            type:int = 0x00
            data:typing.List[int] = []
            crc:int = 0x00
            if (not line.startswith(':')):
                counter_format_error_line += 1
                continue
            # 剔除前面的 :
            line = line[1:]
            # 剔除后面的回车
            line = line.strip()
            
            # 解析每行的数据
            length = int(line[:2], 16)
            address = int(line[2:6], 16)
            type = int(line[6:8], 16)
            data = [int(line[i:i+2], 16) for i in range(8, 8+length*2, 2)]
            crc = int(line[-2:], 16)
            
            if (type == 0x04):
                current_start_linear_address = 0x00000000
                # 扩展线性地址
                for i in range(length):
                    current_start_linear_address |= (data[i])
                    if (i < length-1):
                        current_start_linear_address <<= 8
                current_start_linear_address <<= 16
                if (current_start_linear_address not in self._linear_address_map_memory_data.keys()):
                    self._linear_address_map_memory_data[current_start_linear_address] = MemoryData(
                        address_start=current_start_linear_address,
                        data_list=[],
                        address_end=0,
                    )
                continue
            if (type == 0x00):
                # 添加到地址和数据列表的 mapping 中
                self._linear_address_map_memory_data[current_start_linear_address].data_list.extend(data)
                
                # 添加地址和数据的 mapping 中
                for byte_index, byte in enumerate(data):
                    abs_byte_index:int = current_start_linear_address | address | byte_index
                    if (prev_abs_byte_address != abs_byte_index-1):
                        self._logger.info("开始一个新内存区域, 区域的起始地址为: 0x{0:08X}".format(abs_byte_index))
                    prev_abs_byte_address = abs_byte_index
            else:
                self._logger.error("type 未知类型: 0x{0:02X}".format(type))
        f.close()
        
        # 写数据的结束地址
        for memory_data in self._linear_address_map_memory_data.values():
            memory_data.address_end = memory_data.address_start + len(memory_data.data_list) - 1
        
    def get_summery(self) -> str:
        """获取当前 hex 的总结性信息

        Returns:
            str: 总结性信息
        
        ChangeLog:
            - 2025/1/5: 暂时将该函数废弃，因为之前为了快速实现，重新读了文件，后续需要直接从解析后的数据结构中直接获取
        """
        summery:str = ""                                         # 最后的总结
        counter_format_error_line:int = 0                        # 格式错误的行数
        counter_data_line:int = 0                                # 数据行数
        counter_file_end_line:int = 0                            # 文件结束行数
        counter_ext_address_line:int = 0                         # 扩展地址行数
        counter_start_address_line:int = 0                       # 起始地址行数
        counter_ext_linear_address_line:int = 0                  # 扩展线性地址行数
        counter_start_linear_address_line:int = 0                # 起始线性地址行数
        counter_unknown_line:int = 0                             # 未知类型行数

        ext_linear_address_line_list:typing.List[int] = []       # 扩展线性地址列表
        start_linear_address_line_list:typing.List[int] = []     # 起始线性地址列表
        file_end_line_list:typing.List[int] = []                 # 文件结束行列表  
        
        with open(self._hex_path, 'r') as f:
            lines = f.readlines()
            for index, line in enumerate(lines):
                length:int = 0x00
                address:int = 0x00
                type:int = 0x00
                data:typing.List[int] = []
                crc:int = 0x00
                if (not line.startswith(':')):
                    counter_format_error_line += 1
                    continue
                # 剔除前面的 :
                line = line[1:]
                # 剔除后面的回车
                line = line.strip()
                
                # 解析每行的数据
                length = int(line[:2], 16)
                address = int(line[2:6], 16)
                type = int(line[6:8], 16)
                data = [int(line[i:i+2], 16) for i in range(8, len(line), 2)]
                crc = int(line[-2:], 16)
                
                # TODO: 判断 crc 是否正确
                
                # 统计数据行数
                if (type == 0x00):
                    counter_data_line += 1
                elif (type == 0x01):
                    counter_file_end_line += 1
                    file_end_line_list.append(index + 1)
                elif (type == 0x02):
                    counter_ext_address_line += 1
                elif (type == 0x03):
                    counter_start_address_line += 1
                elif (type == 0x04):
                    counter_ext_linear_address_line += 1
                    ext_linear_address_line_list.append(index+1)
                elif (type == 0x05):
                    counter_start_linear_address_line += 1
                    start_linear_address_line_list.append(index+1)
                else:
                    counter_unknown_line += 1
        
        # 输出总结
        summery += "----------------------------------------\n"
        summery += "文件路径: {}\n".format(self._hex_path)
        summery += "格式错误的行数: {}\n".format(counter_format_error_line)
        summery += "数据行数 0x00: {}\n".format(counter_data_line)
        summery += "文件结束行数 0x01: {}\n".format(counter_file_end_line)
        summery += "扩展地址行数 0x02: {}\n".format(counter_ext_address_line)
        summery += "起始地址行数 0x03: {}\n".format(counter_start_address_line)
        summery += "扩展线性地址行数 0x04: {}\n".format(counter_ext_linear_address_line)
        summery += "起始线性地址行数 0x05: {}\n".format(counter_start_linear_address_line)
        summery += "未知类型行数: {}\n".format(counter_unknown_line)
        
        summery += "文件结束行列表 0x01: {}\n".format(file_end_line_list)
        summery += "扩展线性地址列表 0x04: {}\n".format(ext_linear_address_line_list)
        summery += "起始线性地址列表 0x05: {}\n".format(start_linear_address_line_list)

        return summery
    
    def calculate_crc(self, address_begin:int, length:int, crc_type:CRCType) -> int:
        crc_check_list = self.get_data(address_begin, length)
        res = crc_cal(crc_type, crc_check_list)
        return res
    
    @staticmethod
    def build_hex_file(memory_data_list:typing.List[MemoryData], file_path:str) -> None:
        """_summary_

        Args:
            start_address (int): _description_
            data_list (typing.List[int]): _description_
            file_path (str): _description_
        """
        logger = get_logger()
        logger.info("开始生成 hex 文件")

        hex_s = ""
        min_address = 0xFFFFFFFF
        for memory_data in memory_data_list:
            # 写起始线性地址
            address_start = memory_data.address_start
            segment_address = address_start >> 16
            this_data_list = memory_data.data_list
            vaild_data_length:int = len(memory_data.data_list)
            total_data_length:int = int((vaild_data_length // 0x20 + 1) * 0x20)
            this_data_list.extend([0xFF] * (total_data_length - vaild_data_length))
            segment_count:int = len(this_data_list) // 0xFFFF + 1
            
            # 计算每一段的起始地址和结束地址
            segment_address_list:typing.List[typing.Tuple[int, int]] = []
            for i in range(segment_count):
                start_address = address_start + i * 0x10000
                if (i == segment_count-1):
                    end_address = address_start + total_data_length
                else:
                    end_address = start_address + 0x10000
                segment_address_list.append((start_address, end_address))
            
            # 开始生成 hex 文件内容
            logger.debug(f"segment_address_list: {segment_address_list}")
            for address_segment in segment_address_list:
                begin_address, end_address = address_segment
                logger.debug(f"begin_address: {begin_address:08X}, end_address: {end_address:08X}")
                hex_s += HexIO._dump_extended_segment_address_record(begin_address)
                if (address_start < min_address):
                    min_address = address_start
                # 写每行的数据
                for i in range(begin_address, end_address, 0x20):
                    data_record_begin = i - address_start
                    data_record_end = i + 0x20 - address_start
                    try:
                        hex_s += HexIO._dump_data_record(i, this_data_list[data_record_begin:data_record_end])
                    except Exception as e:
                        logger.error(f"dump data record error: {e}")
                        logger.error(f"data_record_begin: {data_record_begin:08X}, data_record_end: {data_record_end:08X}")
                        raise e

        # 写 hex 起始地址
        hex_s += HexIO._dump_start_linear_address_record(min_address)
        
        # 写文件结束标识
        hex_s += HexIO._dump_end_of_file_record()
        
        # 写文件
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(hex_s)
    
    def get_data(self, address_begin:int, length:int) -> typing.List[int]:
        """
        获取指定地址范围的数据

        Args:
            address_begin (int): 起始地址
            length (int): 数据长度

        Returns:
            typing.List[int]: 数据列表
        """
        res = []
        address_start = self._get_start_address_by_address(address_begin)
        if (address_start == -1):
            return res
        
        return self._linear_address_map_memory_data[address_start].\
            data_list[address_begin-address_start:address_begin-address_start+length]
    
    def set_data(self, address_begin:int, buffer:typing.List[int]) -> None:
        """
        设置指定地址范围的数据

        Args:
            address_begin (int): 起始地址
            buffer (typing.List[int]): 要设置的数据列表
        """
        address_start = self._get_start_address_by_address(address_begin)
        if (address_start == -1):
            self._logger.error(r"找不到包含地址 0x{0:08X} 的内存数据段")
            return
        
        # self._logger.debug(f"设置数据前 data_list 的长度: {len(self._linear_address_map_memory_data[address_start].data_list)}")
        self._linear_address_map_memory_data[address_start].\
            data_list[address_begin-address_start:address_begin-address_start+len(buffer)] = buffer
        # self._logger.debug(f"设置数据后 data_list 的长度: {len(self._linear_address_map_memory_data[address_start].data_list)}")
    
    def _get_start_address_by_address(self, address:int) -> int:
        """
        根据地址获取所在地址段的起始地址

        Args:
            address (int): 实际要查询的地址

        Returns:
            int: 查询出来的起始地址，如果没有找到，则返回 -1
        """
        for memory_data in self._linear_address_map_memory_data.values():
            if (address <= memory_data.address_end and address >= memory_data.address_start):
                return memory_data.address_start
        return -1
    
    @staticmethod
    def _dump_start_linear_address_record(start_linear_address:int) -> str:
        """
        将起始线性地址记录标识转换为字符串，该地址是 APP 的启动地址
        record_type: 0x05
        一行的长度为 9

        Args:
            start_linear_address (int): APP 的启动地址，在这里默认就是和观测区域的地址地址相同

        Returns:
            str: 起始线性地址的 hex 描述字符串
        """
        res = ""
        record_data = [0x00] * 9
        record_data[0] = 0x04
        record_data[3] = 0x05
        record_data[4] = (start_linear_address >> 24) & 0xFF
        record_data[5] = (start_linear_address >> 16) & 0xFF
        record_data[6] = (start_linear_address >> 8) & 0xFF
        record_data[7] = start_linear_address & 0xFF
        record_data[8] = HexIO._calculate_hex_crc(record_data[0:8])
        res = ":" + "".join("{:02X}".format(i) for i in record_data) + "\n"
        return res
    
    @staticmethod
    def _dump_end_of_file_record() -> str:
        """
        返回 hex 文件结束标识
        record_type: 0x01
        一行的长度为 5

        Returns:
            str: _description_
        """
        res = ":00000001FF"
        return res
    
    @staticmethod
    def _dump_extended_segment_address_record(address:int) -> str:
        """
        返回扩展地址记录标识字符串
        record_type: 0x04
        一行的长度为 7

        Args:
            address (int): 扩展地址的

        Returns:
            str: _description_
        """
        res = ""
        record_data = [0x00] * 7
        record_data[0x00] = 0x02
        record_data[0x01] = 0x00
        record_data[0x02] = 0x00
        record_data[0x03] = 0x04
        record_data[0x04:0x06] = [(address >> 24) & 0xFF, address >> 16 & 0xFF]
        record_data[0x06] = HexIO._calculate_hex_crc(record_data[0x00:0x06])
        res = ":" + "".join("{:02X}".format(i) for i in record_data) + "\n"
        return res
    
    @staticmethod
    def _dump_data_record(address:int, data_list:typing.List[int]) -> str:
        """
        返回数据记录标识字符串
        record_type: 0x00
        一行的长度为 37

        Args:
            address (int): 本条记录的数据起始地址
            data_list (typing.List[int]): 本条记录的数据列表

        Returns:
            str: 
        """
        res = ""
        record_data = [0x00] * 37
        record_data[0x00] = 0x20
        record_data[0x01:0x03] = [(address >> 8) & 0xFF, address & 0xFF]
        record_data[0x03] = 0x00
        record_data[0x04:0x24] = data_list
        record_data[36] = HexIO._calculate_hex_crc(record_data[0:36])
        res = ":" + "".join("{:02X}".format(i) for i in record_data) + "\n"
        return res
    
    @staticmethod
    def _calculate_hex_crc(data_list:typing.List[int]) -> int:
        """计算 hex 文件一行的 CRC

        Args:
            data_list (typing.List[int]): _description_

        Returns:
            int: _description_
        """
        res = 0
        for i in data_list:
            res = (res + i) & 0xFF
        
        # 取补码
        res = (~res + 1) & 0xFF
        return res
    